{"cells":[{"cell_type":"markdown","id":"f2605a68-4ec8-40c5-aefc-e5ae7b23b884","metadata":{},"source":["# 使用自查询检索构建酒店客房搜索\n","\n","在这个示例中，我们将介绍如何构建和迭代一个酒店客房搜索服务，该服务利用LLM生成结构化的过滤查询，然后将这些查询传递给向量存储。\n","\n","要了解自查询检索的介绍，请查看[文档](https://python.langchain.com/docs/modules/data_connection/retrievers/self_query)。"]},{"cell_type":"markdown","id":"d621de99-d993-4f4b-b94a-d02b2c7ad4e0","metadata":{},"source":["## 导入和数据准备\n","\n","在这个示例中，我们使用 `ChatOpenAI` 作为模型，`ElasticsearchStore` 作为向量存储，但可以用LLM/ChatModel和[支持自查询的任何VectorStore](https://python.langchain.com/docs/integrations/retrievers/self_query/)进行替换。\n","\n","从以下链接下载数据：https://www.kaggle.com/datasets/keshavramaiah/hotel-recommendation"]},{"cell_type":"code","execution_count":null,"id":"8ecd1fbb-bdba-420b-bcc7-5ea8a232ab11","metadata":{},"outputs":[],"source":["# 安装所需的Python包\n","!pip install langchain langchain-elasticsearch lark openai elasticsearch pandas"]},{"cell_type":"code","execution_count":1,"id":"14d48ff6-2552-4b95-95a9-42dd444471d9","metadata":{},"outputs":[],"source":["# 导入 pandas 库，约定别名为 pd\n","import pandas as pd"]},{"cell_type":"code","execution_count":2,"id":"b852ec6e-7bf6-405e-ae7f-f457eb6e17f1","metadata":{},"outputs":[],"source":["# 读取Hotel_details.csv文件并去除重复的hotelid行，然后将hotelid列设为索引\n","details = (\n","    pd.read_csv(\"~/Downloads/archive/Hotel_details.csv\")\n","    .drop_duplicates(subset=\"hotelid\")\n","    .set_index(\"hotelid\")\n",")\n","\n","# 读取Hotel_Room_attributes.csv文件，将id列设为索引\n","attributes = pd.read_csv(\n","    \"~/Downloads/archive/Hotel_Room_attributes.csv\", index_col=\"id\"\n",")\n","\n","# 读取hotels_RoomPrice.csv文件，将id列设为索引\n","price = pd.read_csv(\"~/Downloads/archive/hotels_RoomPrice.csv\", index_col=\"id\")"]},{"cell_type":"code","execution_count":3,"id":"35a32177-2ca5-4d10-b8dc-f34c25795630","metadata":{},"outputs":[{"data":{"text/html":["<div>\n","<style scoped>\n","    .dataframe tbody tr th:only-of-type {\n","        vertical-align: middle;\n","    }\n","\n","    .dataframe tbody tr th {\n","        vertical-align: top;\n","    }\n","\n","    .dataframe thead th {\n","        text-align: right;\n","    }\n","</style>\n","<table border=\"1\" class=\"dataframe\">\n","  <thead>\n","    <tr style=\"text-align: right;\">\n","      <th></th>\n","      <th>roomtype</th>\n","      <th>onsiterate</th>\n","      <th>roomamenities</th>\n","      <th>maxoccupancy</th>\n","      <th>roomdescription</th>\n","      <th>hotelname</th>\n","      <th>city</th>\n","      <th>country</th>\n","      <th>starrating</th>\n","      <th>mealsincluded</th>\n","    </tr>\n","  </thead>\n","  <tbody>\n","    <tr>\n","      <th>0</th>\n","      <td>Vacation Home</td>\n","      <td>636.09</td>\n","      <td>Air conditioning: ;Closet: ;Fireplace: ;Free W...</td>\n","      <td>4</td>\n","      <td>Shower, Kitchenette, 2 bedrooms, 1 double bed ...</td>\n","      <td>Pantlleni</td>\n","      <td>Beddgelert</td>\n","      <td>United Kingdom</td>\n","      <td>3</td>\n","      <td>False</td>\n","    </tr>\n","    <tr>\n","      <th>1</th>\n","      <td>Vacation Home</td>\n","      <td>591.74</td>\n","      <td>Air conditioning: ;Closet: ;Dishwasher: ;Firep...</td>\n","      <td>4</td>\n","      <td>Shower, Kitchenette, 2 bedrooms, 1 double bed ...</td>\n","      <td>Willow Cottage</td>\n","      <td>Beverley</td>\n","      <td>United Kingdom</td>\n","      <td>3</td>\n","      <td>False</td>\n","    </tr>\n","    <tr>\n","      <th>2</th>\n","      <td>Guest room, Queen or Twin/Single Bed(s)</td>\n","      <td>0.00</td>\n","      <td>NaN</td>\n","      <td>2</td>\n","      <td>NaN</td>\n","      <td>AC Hotel Manchester Salford Quays</td>\n","      <td>Manchester</td>\n","      <td>United Kingdom</td>\n","      <td>4</td>\n","      <td>False</td>\n","    </tr>\n","    <tr>\n","      <th>3</th>\n","      <td>Bargemaster King Accessible Room</td>\n","      <td>379.08</td>\n","      <td>Air conditioning: ;Free Wi-Fi in all rooms!: ;...</td>\n","      <td>2</td>\n","      <td>Shower</td>\n","      <td>Lincoln Plaza London, Curio Collection by Hilton</td>\n","      <td>London</td>\n","      <td>United Kingdom</td>\n","      <td>4</td>\n","      <td>True</td>\n","    </tr>\n","    <tr>\n","      <th>4</th>\n","      <td>Twin Room</td>\n","      <td>156.17</td>\n","      <td>Additional toilet: ;Air conditioning: ;Blackou...</td>\n","      <td>2</td>\n","      <td>Room size: 15 m²/161 ft², Non-smoking, Shower,...</td>\n","      <td>Ibis London Canning Town</td>\n","      <td>London</td>\n","      <td>United Kingdom</td>\n","      <td>3</td>\n","      <td>True</td>\n","    </tr>\n","  </tbody>\n","</table>\n","</div>"],"text/plain":["                                  roomtype  onsiterate  \\\n","0                            Vacation Home      636.09   \n","1                            Vacation Home      591.74   \n","2  Guest room, Queen or Twin/Single Bed(s)        0.00   \n","3         Bargemaster King Accessible Room      379.08   \n","4                                Twin Room      156.17   \n","\n","                                       roomamenities  maxoccupancy  \\\n","0  Air conditioning: ;Closet: ;Fireplace: ;Free W...             4   \n","1  Air conditioning: ;Closet: ;Dishwasher: ;Firep...             4   \n","2                                                NaN             2   \n","3  Air conditioning: ;Free Wi-Fi in all rooms!: ;...             2   \n","4  Additional toilet: ;Air conditioning: ;Blackou...             2   \n","\n","                                     roomdescription  \\\n","0  Shower, Kitchenette, 2 bedrooms, 1 double bed ...   \n","1  Shower, Kitchenette, 2 bedrooms, 1 double bed ...   \n","2                                                NaN   \n","3                                             Shower   \n","4  Room size: 15 m²/161 ft², Non-smoking, Shower,...   \n","\n","                                          hotelname        city  \\\n","0                                         Pantlleni  Beddgelert   \n","1                                    Willow Cottage    Beverley   \n","2                 AC Hotel Manchester Salford Quays  Manchester   \n","3  Lincoln Plaza London, Curio Collection by Hilton      London   \n","4                          Ibis London Canning Town      London   \n","\n","          country  starrating  mealsincluded  \n","0  United Kingdom           3          False  \n","1  United Kingdom           3          False  \n","2  United Kingdom           4          False  \n","3  United Kingdom           4           True  \n","4  United Kingdom           3           True  "]},"execution_count":3,"metadata":{},"output_type":"execute_result"}],"source":["# 将最新的价格数据按照\"refid\"列去重，保留最后一条数据\n","latest_price = price.drop_duplicates(subset=\"refid\", keep=\"last\")[\n","    [\n","        \"hotelcode\",\n","        \"roomtype\",\n","        \"onsiterate\",\n","        \"roomamenities\",\n","        \"maxoccupancy\",\n","        \"mealinclusiontype\",\n","    ]\n","]\n","\n","# 从属性数据中获取\"ratedescription\"列的值，并添加到最新的价格数据中\n","latest_price[\"ratedescription\"] = attributes.loc[latest_price.index][\"ratedescription\"]\n","\n","# 将最新的价格数据与详情数据中的[\"hotelname\", \"city\", \"country\", \"starrating\"]列进行连接\n","latest_price = latest_price.join(\n","    details[[\"hotelname\", \"city\", \"country\", \"starrating\"]], on=\"hotelcode\"\n",")\n","\n","# 重命名列名\"ratedescription\"为\"roomdescription\"\n","latest_price = latest_price.rename({\"ratedescription\": \"roomdescription\"}, axis=1)\n","\n","# 添加新列\"mealsincluded\"，表示是否包含餐食\n","latest_price[\"mealsincluded\"] = ~latest_price[\"mealinclusiontype\"].isnull()\n","\n","# 删除列\"hotelcode\"和\"mealinclusiontype\"\n","latest_price.pop(\"hotelcode\")\n","latest_price.pop(\"mealinclusiontype\")\n","\n","# 重置索引并返回一个新的DataFrame\n","latest_price = latest_price.reset_index(drop=True)\n","\n","# 显示处理后的最新价格数据的前几行\n","latest_price.head()"]},{"cell_type":"markdown","id":"1e4742af-c178-4cf7-a548-b97b3e37bd55","metadata":{},"source":["## 描述数据属性\n","\n","我们将使用一个自查询检索器，这需要我们描述可以进行过滤的元数据。\n","\n","或者，如果我们感到懒惰，我们可以让模型为我们撰写描述的草稿 :)"]},{"cell_type":"code","execution_count":4,"id":"5e2cb352-9111-47b8-9808-37228ba81f87","metadata":{},"outputs":[],"source":["# 导入ChatOpenAI类\n","from langchain_openai import ChatOpenAI\n","\n","# 创建ChatOpenAI对象，指定模型为\"gpt-4\"\n","model = ChatOpenAI(model=\"gpt-4\")\n","\n","# 使用模型预测，传入包含酒店房间信息的字符串，生成JSON列表\n","res = model.predict(\n","    \"Below is a table with information about hotel rooms. \"\n","    \"Return a JSON list with an entry for each column. Each entry should have \"\n","    '{\"name\": \"column name\", \"description\": \"column description\", \"type\": \"column data type\"}'\n","    f\"\\n\\n{latest_price.head()}\\n\\nJSON:\\n\"\n",")"]},{"cell_type":"code","execution_count":5,"id":"d831664d-68cd-4dba-aad2-9248f10c7663","metadata":{},"outputs":[{"data":{"text/plain":["[{'name': 'roomtype', 'description': 'The type of the room', 'type': 'string'},\n"," {'name': 'onsiterate',\n","  'description': 'The rate of the room',\n","  'type': 'float'},\n"," {'name': 'roomamenities',\n","  'description': 'Amenities available in the room',\n","  'type': 'string'},\n"," {'name': 'maxoccupancy',\n","  'description': 'Maximum number of people that can occupy the room',\n","  'type': 'integer'},\n"," {'name': 'roomdescription',\n","  'description': 'Description of the room',\n","  'type': 'string'},\n"," {'name': 'hotelname', 'description': 'Name of the hotel', 'type': 'string'},\n"," {'name': 'city',\n","  'description': 'City where the hotel is located',\n","  'type': 'string'},\n"," {'name': 'country',\n","  'description': 'Country where the hotel is located',\n","  'type': 'string'},\n"," {'name': 'starrating',\n","  'description': 'Star rating of the hotel',\n","  'type': 'integer'},\n"," {'name': 'mealsincluded',\n","  'description': 'Whether meals are included or not',\n","  'type': 'boolean'}]"]},"execution_count":5,"metadata":{},"output_type":"execute_result"}],"source":["import json\n","\n","# 使用json.loads()方法将res转换为Python对象\n","attribute_info = json.loads(res)\n","# 打印转换后的Python对象\n","attribute_info"]},{"cell_type":"markdown","id":"aadb16c5-9f70-4bcc-b4fa-1af31bc8e38a","metadata":{},"source":["对于低基数特征，让我们在描述中包含有效值。"]},{"cell_type":"code","execution_count":6,"id":"cce77f43-980a-4ab6-923a-0f9d70a093d6","metadata":{},"outputs":[{"data":{"text/plain":["maxoccupancy     19\n","country          29\n","starrating        3\n","mealsincluded     2\n","dtype: int64"]},"execution_count":6,"metadata":{},"output_type":"execute_result"}],"source":["# 获取最新价格的唯一值数量，并筛选出唯一值数量小于40的数据\n","latest_price.nunique()[latest_price.nunique() < 40]"]},{"cell_type":"code","execution_count":7,"id":"2db33ed8-4f91-4a2d-9613-9dd6c9fcdbcb","metadata":{},"outputs":[],"source":["# 将最新价格数据集中'starrating'列的唯一值按照升序排列后，添加到attribute_info列表倒数第二个元素的\"description\"字段中\n","attribute_info[-2][\"description\"] += (\n","    f\". Valid values are {sorted(latest_price['starrating'].value_counts().index.tolist())}\"\n",")\n","\n","# 将最新价格数据集中'maxoccupancy'列的唯一值按照升序排列后，添加到attribute_info列表第4个元素的\"description\"字段中\n","attribute_info[3][\"description\"] += (\n","    f\". Valid values are {sorted(latest_price['maxoccupancy'].value_counts().index.tolist())}\"\n",")\n","\n","# 将最新价格数据集中'country'列的唯一值按照升序排列后，添加到attribute_info列表倒数第3个元素的\"description\"字段中\n","attribute_info[-3][\"description\"] += (\n","    f\". Valid values are {sorted(latest_price['country'].value_counts().index.tolist())}\"\n",")"]},{"cell_type":"code","execution_count":8,"id":"89c7461b-e6f7-4608-9929-ae952fb3348c","metadata":{},"outputs":[{"data":{"text/plain":["[{'name': 'roomtype', 'description': 'The type of the room', 'type': 'string'},\n"," {'name': 'onsiterate',\n","  'description': 'The rate of the room',\n","  'type': 'float'},\n"," {'name': 'roomamenities',\n","  'description': 'Amenities available in the room',\n","  'type': 'string'},\n"," {'name': 'maxoccupancy',\n","  'description': 'Maximum number of people that can occupy the room. Valid values are [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 20, 24]',\n","  'type': 'integer'},\n"," {'name': 'roomdescription',\n","  'description': 'Description of the room',\n","  'type': 'string'},\n"," {'name': 'hotelname', 'description': 'Name of the hotel', 'type': 'string'},\n"," {'name': 'city',\n","  'description': 'City where the hotel is located',\n","  'type': 'string'},\n"," {'name': 'country',\n","  'description': \"Country where the hotel is located. Valid values are ['Austria', 'Belgium', 'Bulgaria', 'Croatia', 'Cyprus', 'Czech Republic', 'Denmark', 'Estonia', 'Finland', 'France', 'Germany', 'Greece', 'Hungary', 'Ireland', 'Italy', 'Latvia', 'Lithuania', 'Luxembourg', 'Malta', 'Netherlands', 'Poland', 'Portugal', 'Romania', 'Slovakia', 'Slovenia', 'Spain', 'Sweden', 'Switzerland', 'United Kingdom']\",\n","  'type': 'string'},\n"," {'name': 'starrating',\n","  'description': 'Star rating of the hotel. Valid values are [2, 3, 4]',\n","  'type': 'integer'},\n"," {'name': 'mealsincluded',\n","  'description': 'Whether meals are included or not',\n","  'type': 'boolean'}]"]},"execution_count":8,"metadata":{},"output_type":"execute_result"}],"source":["attribute_info"]},{"cell_type":"markdown","id":"81c75a25-9c64-4da6-87ae-580bd47962bb","metadata":{},"source":["## 创建查询构造器链\n","\n","让我们来看一下将自然语言请求转换为结构化查询的链。\n","\n","首先，我们只需加载提示并查看其外观。"]},{"cell_type":"code","execution_count":9,"id":"b960f5f4-75f7-4a93-959f-b5293986b864","metadata":{},"outputs":[],"source":["from langchain.chains.query_constructor.base import (\n","    get_query_constructor_prompt,\n","    load_query_constructor_runnable,\n",")"]},{"cell_type":"code","execution_count":10,"id":"bc85c90d-08fc-444f-b912-c6b2ac089bfd","metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":["Your goal is to structure the user's query to match the request schema provided below.\n","\n","<< Structured Request Schema >>\n","When responding use a markdown code snippet with a JSON object formatted in the following schema:\n","\n","```json\n","{\n","    \"query\": string \\ text string to compare to document contents\n","    \"filter\": string \\ logical condition statement for filtering documents\n","}\n","```\n","\n","The query string should contain only text that is expected to match the contents of documents. Any conditions in the filter should not be mentioned in the query as well.\n","\n","A logical condition statement is composed of one or more comparison and logical operation statements.\n","\n","A comparison statement takes the form: `comp(attr, val)`:\n","- `comp` (eq | ne | gt | gte | lt | lte | contain | like | in | nin): comparator\n","- `attr` (string):  name of attribute to apply the comparison to\n","- `val` (string): is the comparison value\n","\n","A logical operation statement takes the form `op(statement1, statement2, ...)`:\n","- `op` (and | or | not): logical operator\n","- `statement1`, `statement2`, ... (comparison statements or logical operation statements): one or more statements to apply the operation to\n","\n","Make sure that you only use the comparators and logical operators listed above and no others.\n","Make sure that filters only refer to attributes that exist in the data source.\n","Make sure that filters only use the attributed names with its function names if there are functions applied on them.\n","Make sure that filters only use format `YYYY-MM-DD` when handling timestamp data typed values.\n","Make sure that filters take into account the descriptions of attributes and only make comparisons that are feasible given the type of data being stored.\n","Make sure that filters are only used as needed. If there are no filters that should be applied return \"NO_FILTER\" for the filter value.\n","\n","<< Example 1. >>\n","Data Source:\n","```json\n","{\n","    \"content\": \"Lyrics of a song\",\n","    \"attributes\": {\n","        \"artist\": {\n","            \"type\": \"string\",\n","            \"description\": \"Name of the song artist\"\n","        },\n","        \"length\": {\n","            \"type\": \"integer\",\n","            \"description\": \"Length of the song in seconds\"\n","        },\n","        \"genre\": {\n","            \"type\": \"string\",\n","            \"description\": \"The song genre, one of \"pop\", \"rock\" or \"rap\"\"\n","        }\n","    }\n","}\n","```\n","\n","User Query:\n","What are songs by Taylor Swift or Katy Perry about teenage romance under 3 minutes long in the dance pop genre\n","\n","Structured Request:\n","```json\n","{\n","    \"query\": \"teenager love\",\n","    \"filter\": \"and(or(eq(\\\"artist\\\", \\\"Taylor Swift\\\"), eq(\\\"artist\\\", \\\"Katy Perry\\\")), lt(\\\"length\\\", 180), eq(\\\"genre\\\", \\\"pop\\\"))\"\n","}\n","```\n","\n","\n","<< Example 2. >>\n","Data Source:\n","```json\n","{\n","    \"content\": \"Lyrics of a song\",\n","    \"attributes\": {\n","        \"artist\": {\n","            \"type\": \"string\",\n","            \"description\": \"Name of the song artist\"\n","        },\n","        \"length\": {\n","            \"type\": \"integer\",\n","            \"description\": \"Length of the song in seconds\"\n","        },\n","        \"genre\": {\n","            \"type\": \"string\",\n","            \"description\": \"The song genre, one of \"pop\", \"rock\" or \"rap\"\"\n","        }\n","    }\n","}\n","```\n","\n","User Query:\n","What are songs that were not published on Spotify\n","\n","Structured Request:\n","```json\n","{\n","    \"query\": \"\",\n","    \"filter\": \"NO_FILTER\"\n","}\n","```\n","\n","\n","<< Example 3. >>\n","Data Source:\n","```json\n","{\n","    \"content\": \"Detailed description of a hotel room\",\n","    \"attributes\": {\n","    \"roomtype\": {\n","        \"description\": \"The type of the room\",\n","        \"type\": \"string\"\n","    },\n","    \"onsiterate\": {\n","        \"description\": \"The rate of the room\",\n","        \"type\": \"float\"\n","    },\n","    \"roomamenities\": {\n","        \"description\": \"Amenities available in the room\",\n","        \"type\": \"string\"\n","    },\n","    \"maxoccupancy\": {\n","        \"description\": \"Maximum number of people that can occupy the room. Valid values are [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 20, 24]\",\n","        \"type\": \"integer\"\n","    },\n","    \"roomdescription\": {\n","        \"description\": \"Description of the room\",\n","        \"type\": \"string\"\n","    },\n","    \"hotelname\": {\n","        \"description\": \"Name of the hotel\",\n","        \"type\": \"string\"\n","    },\n","    \"city\": {\n","        \"description\": \"City where the hotel is located\",\n","        \"type\": \"string\"\n","    },\n","    \"country\": {\n","        \"description\": \"Country where the hotel is located. Valid values are ['Austria', 'Belgium', 'Bulgaria', 'Croatia', 'Cyprus', 'Czech Republic', 'Denmark', 'Estonia', 'Finland', 'France', 'Germany', 'Greece', 'Hungary', 'Ireland', 'Italy', 'Latvia', 'Lithuania', 'Luxembourg', 'Malta', 'Netherlands', 'Poland', 'Portugal', 'Romania', 'Slovakia', 'Slovenia', 'Spain', 'Sweden', 'Switzerland', 'United Kingdom']\",\n","        \"type\": \"string\"\n","    },\n","    \"starrating\": {\n","        \"description\": \"Star rating of the hotel. Valid values are [2, 3, 4]\",\n","        \"type\": \"integer\"\n","    },\n","    \"mealsincluded\": {\n","        \"description\": \"Whether meals are included or not\",\n","        \"type\": \"boolean\"\n","    }\n","}\n","}\n","```\n","\n","User Query:\n","{query}\n","\n","Structured Request:\n","\n"]}],"source":["# 定义一个包含酒店房间详细描述的字符串\n","doc_contents = \"Detailed description of a hotel room\"\n","\n","# 调用函数 get_query_constructor_prompt，获取查询构造器的提示信息\n","prompt = get_query_constructor_prompt(doc_contents, attribute_info)\n","\n","# 打印格式化后的提示信息，将\"{query}\"替换为实际的查询内容\n","print(prompt.format(query=\"{query}\"))"]},{"cell_type":"code","execution_count":11,"id":"1e7efcae-7943-4200-be43-5c5117ba1c9d","metadata":{},"outputs":[],"source":["\n","\n","# 使用 ChatOpenAI 类创建一个实例 chain，并传入参数 model=\"gpt-3.5-turbo\" 和 temperature=0\n","chain = load_query_constructor_runnable(\n","    ChatOpenAI(model=\"gpt-3.5-turbo\", temperature=0), doc_contents, attribute_info\n",")"]},{"cell_type":"code","execution_count":12,"id":"74bf0cb2-84a5-45ef-8fc3-cbcffcaf0bbf","metadata":{},"outputs":[{"data":{"text/plain":["StructuredQuery(query='hotel', filter=Operation(operator=<Operator.AND: 'and'>, arguments=[Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='country', value='Italy'), Comparison(comparator=<Comparator.LTE: 'lte'>, attribute='onsiterate', value=200)]), limit=None)"]},"execution_count":12,"metadata":{},"output_type":"execute_result"}],"source":["# 调用chain对象的invoke方法，传入一个包含查询信息的字典作为参数\n","chain.invoke({\"query\": \"I want a hotel in Southern Europe and my budget is 200 bucks.\"})"]},{"cell_type":"code","execution_count":13,"id":"3ad704f3-679b-4dd2-b6c3-b4469ba60848","metadata":{},"outputs":[{"data":{"text/plain":["StructuredQuery(query='2-person room', filter=Operation(operator=<Operator.AND: 'and'>, arguments=[Operation(operator=<Operator.OR: 'or'>, arguments=[Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='city', value='Vienna'), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='city', value='London')]), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='maxoccupancy', value=2), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='mealsincluded', value=True), Comparison(comparator=<Comparator.CONTAIN: 'contain'>, attribute='roomamenities', value='AC')]), limit=None)"]},"execution_count":13,"metadata":{},"output_type":"execute_result"}],"source":["# 调用chain的invoke方法，传入一个字典作为参数\n","chain.invoke(\n","    {\n","        \"query\": \"Find a 2-person room in Vienna or London, preferably with meals included and AC\"\n","    }\n",")"]},{"cell_type":"markdown","id":"109591d0-758a-48ab-b337-41092c6d289f","metadata":{},"source":["## 优化属性描述\n","\n","我们可以看到上面至少有两个问题。首先是当我们要求一个南欧目的地时，我们只得到了对意大利的过滤，其次是当我们要求空调时，我们得到了对AC的字面字符串查找（这并不是太糟糕，但会错过像“空调”这样的内容）。\n","\n","作为第一步，让我们尝试更新我们对“国家”属性的描述，以强调只有在提到特定国家时才应使用相等性。"]},{"cell_type":"code","execution_count":14,"id":"07b6a751-5122-4283-aa32-0f3bbc5e4354","metadata":{},"outputs":[],"source":["# 将描述信息添加到attribute_info列表倒数第三个元素的\"description\"键中\n","attribute_info[-3][\"description\"] += (\n","    \". NOTE: Only use the 'eq' operator if a specific country is mentioned. If a region is mentioned, include all relevant countries in filter.\"\n",")\n","\n","# 调用load_query_constructor_runnable函数，传入ChatOpenAI模型参数和其他参数\n","chain = load_query_constructor_runnable(\n","    ChatOpenAI(model=\"gpt-3.5-turbo\", temperature=0),\n","    doc_contents,\n","    attribute_info,\n",")"]},{"cell_type":"code","execution_count":15,"id":"ca33b44c-29bd-4d63-bb3e-ff8eabe1e86c","metadata":{},"outputs":[{"data":{"text/plain":["StructuredQuery(query='hotel', filter=Operation(operator=<Operator.AND: 'and'>, arguments=[Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='mealsincluded', value=False), Comparison(comparator=<Comparator.LTE: 'lte'>, attribute='onsiterate', value=200), Operation(operator=<Operator.OR: 'or'>, arguments=[Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='country', value='Italy'), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='country', value='Spain'), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='country', value='Greece'), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='country', value='Portugal'), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='country', value='Croatia'), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='country', value='Cyprus'), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='country', value='Malta'), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='country', value='Bulgaria'), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='country', value='Romania'), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='country', value='Slovenia'), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='country', value='Czech Republic'), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='country', value='Slovakia'), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='country', value='Hungary'), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='country', value='Poland'), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='country', value='Estonia'), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='country', value='Latvia'), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='country', value='Lithuania')])]), limit=None)"]},"execution_count":15,"metadata":{},"output_type":"execute_result"}],"source":["# 调用chain对象的invoke方法，并传入一个字典作为参数\n","# 字典包含一个键值对，键为\"query\"，值为\"I want a hotel in Southern Europe and my budget is 200 bucks.\"\n","chain.invoke({\"query\": \"我想在南欧找一家酒店，我的预算是200美元。\"})"]},{"cell_type":"markdown","id":"eb793908-ea10-4a55-96b8-ab6915262c50","metadata":{},"source":["## 精细化筛选属性\n","\n","这似乎有所帮助！现在让我们尝试缩小我们筛选的属性范围。我们可以将更自由形式的属性留给主查询，这样可以更好地捕捉语义含义，而不是搜索特定的子字符串。"]},{"cell_type":"code","execution_count":16,"id":"7ca32075-9361-48c1-b349-511a1dd4f908","metadata":{},"outputs":[],"source":["# 定义一个包含属性名称的列表\n","content_attr = [\"roomtype\", \"roomamenities\", \"roomdescription\", \"hotelname\"]\n","# 定义一个包含酒店房间详细描述的字符串\n","doc_contents = \"A detailed description of a hotel room, including information about the room type and room amenities.\"\n","# 使用列表推导式创建一个过滤后的属性信息元组\n","filter_attribute_info = tuple(\n","    ai for ai in attribute_info if ai[\"name\"] not in content_attr\n",")\n","# 调用load_query_constructor_runnable函数，传入ChatOpenAI模型、文档内容、过滤后的属性信息\n","chain = load_query_constructor_runnable(\n","    ChatOpenAI(model=\"gpt-3.5-turbo\", temperature=0),\n","    doc_contents,\n","    filter_attribute_info,\n",")"]},{"cell_type":"code","execution_count":17,"id":"8eb956af-a799-4267-a098-d443c975ee0f","metadata":{},"outputs":[{"data":{"text/plain":["StructuredQuery(query='2-person room', filter=Operation(operator=<Operator.AND: 'and'>, arguments=[Operation(operator=<Operator.OR: 'or'>, arguments=[Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='city', value='Vienna'), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='city', value='London')]), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='maxoccupancy', value=2), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='mealsincluded', value=True)]), limit=None)"]},"execution_count":17,"metadata":{},"output_type":"execute_result"}],"source":["# 调用chain的invoke方法，传入一个包含查询信息的字典作为参数\n","chain.invoke(\n","    {\n","        \"query\": \"Find a 2-person room in Vienna or London, preferably with meals included and AC\"\n","    }\n",")"]},{"cell_type":"markdown","id":"b0263ad4-aef9-48ce-be66-eabd1999beb3","metadata":{},"source":["## 添加特定于我们用例的示例\n","\n","我们已经移除了对“AC”的严格过滤，但它仍未包含在查询字符串中。我们的链式提示是一个带有一些默认示例的少拍提示。让我们看看添加特定于用例的示例是否会有所帮助："]},{"cell_type":"code","execution_count":18,"id":"62b903c1-3861-4aef-9ea6-1666eeee503c","metadata":{"scrolled":true},"outputs":[{"name":"stdout","output_type":"stream","text":["Your goal is to structure the user's query to match the request schema provided below.\n","\n","<< Structured Request Schema >>\n","When responding use a markdown code snippet with a JSON object formatted in the following schema:\n","\n","```json\n","{\n","    \"query\": string \\ text string to compare to document contents\n","    \"filter\": string \\ logical condition statement for filtering documents\n","}\n","```\n","\n","The query string should contain only text that is expected to match the contents of documents. Any conditions in the filter should not be mentioned in the query as well.\n","\n","A logical condition statement is composed of one or more comparison and logical operation statements.\n","\n","A comparison statement takes the form: `comp(attr, val)`:\n","- `comp` (eq | ne | gt | gte | lt | lte | contain | like | in | nin): comparator\n","- `attr` (string):  name of attribute to apply the comparison to\n","- `val` (string): is the comparison value\n","\n","A logical operation statement takes the form `op(statement1, statement2, ...)`:\n","- `op` (and | or | not): logical operator\n","- `statement1`, `statement2`, ... (comparison statements or logical operation statements): one or more statements to apply the operation to\n","\n","Make sure that you only use the comparators and logical operators listed above and no others.\n","Make sure that filters only refer to attributes that exist in the data source.\n","Make sure that filters only use the attributed names with its function names if there are functions applied on them.\n","Make sure that filters only use format `YYYY-MM-DD` when handling timestamp data typed values.\n","Make sure that filters take into account the descriptions of attributes and only make comparisons that are feasible given the type of data being stored.\n","Make sure that filters are only used as needed. If there are no filters that should be applied return \"NO_FILTER\" for the filter value.\n","\n","<< Data Source >>\n","```json\n","{\n","    \"content\": \"A detailed description of a hotel room, including information about the room type and room amenities.\",\n","    \"attributes\": {\n","    \"onsiterate\": {\n","        \"description\": \"The rate of the room\",\n","        \"type\": \"float\"\n","    },\n","    \"maxoccupancy\": {\n","        \"description\": \"Maximum number of people that can occupy the room. Valid values are [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 20, 24]\",\n","        \"type\": \"integer\"\n","    },\n","    \"city\": {\n","        \"description\": \"City where the hotel is located\",\n","        \"type\": \"string\"\n","    },\n","    \"country\": {\n","        \"description\": \"Country where the hotel is located. Valid values are ['Austria', 'Belgium', 'Bulgaria', 'Croatia', 'Cyprus', 'Czech Republic', 'Denmark', 'Estonia', 'Finland', 'France', 'Germany', 'Greece', 'Hungary', 'Ireland', 'Italy', 'Latvia', 'Lithuania', 'Luxembourg', 'Malta', 'Netherlands', 'Poland', 'Portugal', 'Romania', 'Slovakia', 'Slovenia', 'Spain', 'Sweden', 'Switzerland', 'United Kingdom']. NOTE: Only use the 'eq' operator if a specific country is mentioned. If a region is mentioned, include all relevant countries in filter.\",\n","        \"type\": \"string\"\n","    },\n","    \"starrating\": {\n","        \"description\": \"Star rating of the hotel. Valid values are [2, 3, 4]\",\n","        \"type\": \"integer\"\n","    },\n","    \"mealsincluded\": {\n","        \"description\": \"Whether meals are included or not\",\n","        \"type\": \"boolean\"\n","    }\n","}\n","}\n","```\n","\n","\n","<< Example 1. >>\n","User Query:\n","I want a hotel in the Balkans with a king sized bed and a hot tub. Budget is $300 a night\n","\n","Structured Request:\n","```json\n","{\n","    \"query\": \"king-sized bed, hot tub\",\n","    \"filter\": \"and(in(\\\"country\\\", [\\\"Bulgaria\\\", \\\"Greece\\\", \\\"Croatia\\\", \\\"Serbia\\\"]), lte(\\\"onsiterate\\\", 300))\"\n","}\n","```\n","\n","\n","<< Example 2. >>\n","User Query:\n","A room with breakfast included for 3 people, at a Hilton\n","\n","Structured Request:\n","```json\n","{\n","    \"query\": \"Hilton\",\n","    \"filter\": \"and(eq(\\\"mealsincluded\\\", true), gte(\\\"maxoccupancy\\\", 3))\"\n","}\n","```\n","\n","\n","<< Example 3. >>\n","User Query:\n","{query}\n","\n","Structured Request:\n","\n"]}],"source":["examples = [\n","    (\n","        \"我想要在巴尔干地区的一家带有特大号床和热水浴缸的酒店。预算是每晚300美元\",\n","        {\n","            \"query\": \"特大号床，热水浴缸\",\n","            \"filter\": 'and(in(\"country\", [\"保加利亚\", \"希腊\", \"克罗地亚\", \"塞尔维亚\"]), lte(\"价格\", 300))',\n","        },\n","    ),\n","    (\n","        \"一间希尔顿酒店包含早餐的房间，适合3个人\",\n","        {\n","            \"query\": \"希尔顿\",\n","            \"filter\": 'and(eq(\"包含餐食\", true), gte(\"最大入住人数\", 3))',\n","        },\n","    ),\n","]\n","prompt = get_query_constructor_prompt(\n","    doc_contents, filter_attribute_info, examples=examples\n",")\n","print(prompt.format(query=\"{query}\"))"]},{"cell_type":"code","execution_count":19,"id":"0f27f3eb-7261-4362-8060-58fbdc8beece","metadata":{},"outputs":[],"source":["# 加载查询构造器可运行对象\n","chain = load_query_constructor_runnable(\n","    ChatOpenAI(model=\"gpt-3.5-turbo\", temperature=0),  # 使用ChatOpenAI模型，模型为\"gpt-3.5-turbo\"，温度为0\n","    doc_contents,  # 文档内容\n","    filter_attribute_info,  # 过滤属性信息\n","    examples=examples,  # 示例\n",")"]},{"cell_type":"code","execution_count":20,"id":"5808741d-971a-4bb1-a8f0-c403059df842","metadata":{},"outputs":[{"data":{"text/plain":["StructuredQuery(query='2-person room, meals included, AC', filter=Operation(operator=<Operator.AND: 'and'>, arguments=[Operation(operator=<Operator.OR: 'or'>, arguments=[Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='city', value='Vienna'), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='city', value='London')]), Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='mealsincluded', value=True)]), limit=None)"]},"execution_count":20,"metadata":{},"output_type":"execute_result"}],"source":["# 调用chain对象的invoke方法，传入一个包含查询信息的字典作为参数\n","chain.invoke(\n","    {\n","        \"query\": \"Find a 2-person room in Vienna or London, preferably with meals included and AC\"\n","    }\n",")"]},{"cell_type":"markdown","id":"8d66439f-4a4f-44c7-8b9a-8b2d5d6a3683","metadata":{},"source":["这似乎有所帮助！让我们尝试另一个复杂的查询："]},{"cell_type":"code","execution_count":21,"id":"29ed9602-8950-44c9-aaf8-32b69235eb8c","metadata":{},"outputs":[{"ename":"OutputParserException","evalue":"Parsing text\n```json\n{\n    \"query\": \"highly rated, coast, patio, fireplace\",\n    \"filter\": \"and(eq(\\\"starrating\\\", 4), contain(\\\"description\\\", \\\"coast\\\"), contain(\\\"description\\\", \\\"patio\\\"), contain(\\\"description\\\", \\\"fireplace\\\"))\"\n}\n```\n raised following error:\nReceived invalid attributes description. Allowed attributes are ['onsiterate', 'maxoccupancy', 'city', 'country', 'starrating', 'mealsincluded']","output_type":"error","traceback":["\u001b[0;31m---------------------------------------------------------------------------\u001b[0m","\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)","File \u001b[0;32m~/langchain/libs/langchain/langchain/chains/query_constructor/base.py:53\u001b[0m, in \u001b[0;36mStructuredQueryOutputParser.parse\u001b[0;34m(self, text)\u001b[0m\n\u001b[1;32m     52\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m---> 53\u001b[0m     parsed[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfilter\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mast_parse\u001b[49m\u001b[43m(\u001b[49m\u001b[43mparsed\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mfilter\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m     54\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m parsed\u001b[38;5;241m.\u001b[39mget(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mlimit\u001b[39m\u001b[38;5;124m\"\u001b[39m):\n","File \u001b[0;32m~/langchain/.venv/lib/python3.9/site-packages/lark/lark.py:652\u001b[0m, in \u001b[0;36mLark.parse\u001b[0;34m(self, text, start, on_error)\u001b[0m\n\u001b[1;32m    635\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Parse the given text, according to the options provided.\u001b[39;00m\n\u001b[1;32m    636\u001b[0m \n\u001b[1;32m    637\u001b[0m \u001b[38;5;124;03mParameters:\u001b[39;00m\n\u001b[0;32m   (...)\u001b[0m\n\u001b[1;32m    650\u001b[0m \n\u001b[1;32m    651\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m--> 652\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparser\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparse\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtext\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstart\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstart\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mon_error\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mon_error\u001b[49m\u001b[43m)\u001b[49m\n","File \u001b[0;32m~/langchain/.venv/lib/python3.9/site-packages/lark/parser_frontends.py:101\u001b[0m, in \u001b[0;36mParsingFrontend.parse\u001b[0;34m(self, text, start, on_error)\u001b[0m\n\u001b[1;32m    100\u001b[0m stream \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_make_lexer_thread(text)\n\u001b[0;32m--> 101\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparser\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparse\u001b[49m\u001b[43m(\u001b[49m\u001b[43mstream\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mchosen_start\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkw\u001b[49m\u001b[43m)\u001b[49m\n","File \u001b[0;32m~/langchain/.venv/lib/python3.9/site-packages/lark/parsers/lalr_parser.py:41\u001b[0m, in \u001b[0;36mLALR_Parser.parse\u001b[0;34m(self, lexer, start, on_error)\u001b[0m\n\u001b[1;32m     40\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m---> 41\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparser\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparse\u001b[49m\u001b[43m(\u001b[49m\u001b[43mlexer\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstart\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m     42\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m UnexpectedInput \u001b[38;5;28;01mas\u001b[39;00m e:\n","File \u001b[0;32m~/langchain/.venv/lib/python3.9/site-packages/lark/parsers/lalr_parser.py:171\u001b[0m, in \u001b[0;36m_Parser.parse\u001b[0;34m(self, lexer, start, value_stack, state_stack, start_interactive)\u001b[0m\n\u001b[1;32m    170\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m InteractiveParser(\u001b[38;5;28mself\u001b[39m, parser_state, parser_state\u001b[38;5;241m.\u001b[39mlexer)\n\u001b[0;32m--> 171\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparse_from_state\u001b[49m\u001b[43m(\u001b[49m\u001b[43mparser_state\u001b[49m\u001b[43m)\u001b[49m\n","File \u001b[0;32m~/langchain/.venv/lib/python3.9/site-packages/lark/parsers/lalr_parser.py:184\u001b[0m, in \u001b[0;36m_Parser.parse_from_state\u001b[0;34m(self, state, last_token)\u001b[0m\n\u001b[1;32m    183\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m token \u001b[38;5;129;01min\u001b[39;00m state\u001b[38;5;241m.\u001b[39mlexer\u001b[38;5;241m.\u001b[39mlex(state):\n\u001b[0;32m--> 184\u001b[0m     \u001b[43mstate\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfeed_token\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtoken\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    186\u001b[0m end_token \u001b[38;5;241m=\u001b[39m Token\u001b[38;5;241m.\u001b[39mnew_borrow_pos(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m$END\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m'\u001b[39m, token) \u001b[38;5;28;01mif\u001b[39;00m token \u001b[38;5;28;01melse\u001b[39;00m Token(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m$END\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;241m0\u001b[39m, \u001b[38;5;241m1\u001b[39m, \u001b[38;5;241m1\u001b[39m)\n","File \u001b[0;32m~/langchain/.venv/lib/python3.9/site-packages/lark/parsers/lalr_parser.py:150\u001b[0m, in \u001b[0;36mParserState.feed_token\u001b[0;34m(self, token, is_end)\u001b[0m\n\u001b[1;32m    148\u001b[0m     s \u001b[38;5;241m=\u001b[39m []\n\u001b[0;32m--> 150\u001b[0m value \u001b[38;5;241m=\u001b[39m \u001b[43mcallbacks\u001b[49m\u001b[43m[\u001b[49m\u001b[43mrule\u001b[49m\u001b[43m]\u001b[49m\u001b[43m(\u001b[49m\u001b[43ms\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    152\u001b[0m _action, new_state \u001b[38;5;241m=\u001b[39m states[state_stack[\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m]][rule\u001b[38;5;241m.\u001b[39morigin\u001b[38;5;241m.\u001b[39mname]\n","File \u001b[0;32m~/langchain/.venv/lib/python3.9/site-packages/lark/parse_tree_builder.py:153\u001b[0m, in \u001b[0;36mChildFilterLALR_NoPlaceholders.__call__\u001b[0;34m(self, children)\u001b[0m\n\u001b[1;32m    152\u001b[0m         filtered\u001b[38;5;241m.\u001b[39mappend(children[i])\n\u001b[0;32m--> 153\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mnode_builder\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfiltered\u001b[49m\u001b[43m)\u001b[49m\n","File \u001b[0;32m~/langchain/.venv/lib/python3.9/site-packages/lark/parse_tree_builder.py:325\u001b[0m, in \u001b[0;36mapply_visit_wrapper.<locals>.f\u001b[0;34m(children)\u001b[0m\n\u001b[1;32m    323\u001b[0m \u001b[38;5;129m@wraps\u001b[39m(func)\n\u001b[1;32m    324\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mf\u001b[39m(children):\n\u001b[0;32m--> 325\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mwrapper\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mname\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mchildren\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m)\u001b[49m\n","File \u001b[0;32m~/langchain/.venv/lib/python3.9/site-packages/lark/visitors.py:501\u001b[0m, in \u001b[0;36m_vargs_inline\u001b[0;34m(f, _data, children, _meta)\u001b[0m\n\u001b[1;32m    500\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_vargs_inline\u001b[39m(f, _data, children, _meta):\n\u001b[0;32m--> 501\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mf\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mchildren\u001b[49m\u001b[43m)\u001b[49m\n","File \u001b[0;32m~/langchain/.venv/lib/python3.9/site-packages/lark/visitors.py:479\u001b[0m, in \u001b[0;36m_VArgsWrapper.__call__\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m    478\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__call__\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[0;32m--> 479\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbase_func\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n","File \u001b[0;32m~/langchain/libs/langchain/langchain/chains/query_constructor/parser.py:79\u001b[0m, in \u001b[0;36mQueryTransformer.func_call\u001b[0;34m(self, func_name, args)\u001b[0m\n\u001b[1;32m     78\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mallowed_attributes \u001b[38;5;129;01mand\u001b[39;00m args[\u001b[38;5;241m0\u001b[39m] \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mallowed_attributes:\n\u001b[0;32m---> 79\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m     80\u001b[0m         \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mReceived invalid attributes \u001b[39m\u001b[38;5;132;01m{\u001b[39;00margs[\u001b[38;5;241m0\u001b[39m]\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m. Allowed attributes are \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m     81\u001b[0m         \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mallowed_attributes\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m     82\u001b[0m     )\n\u001b[1;32m     83\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m Comparison(comparator\u001b[38;5;241m=\u001b[39mfunc, attribute\u001b[38;5;241m=\u001b[39margs[\u001b[38;5;241m0\u001b[39m], value\u001b[38;5;241m=\u001b[39margs[\u001b[38;5;241m1\u001b[39m])\n","\u001b[0;31mValueError\u001b[0m: Received invalid attributes description. Allowed attributes are ['onsiterate', 'maxoccupancy', 'city', 'country', 'starrating', 'mealsincluded']","\nDuring handling of the above exception, another exception occurred:\n","\u001b[0;31mOutputParserException\u001b[0m                     Traceback (most recent call last)","Cell \u001b[0;32mIn[21], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mchain\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\u001b[43m{\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mquery\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mI want to stay somewhere highly rated along the coast. I want a room with a patio and a fireplace.\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m}\u001b[49m\u001b[43m)\u001b[49m\n","File \u001b[0;32m~/langchain/libs/langchain/langchain/schema/runnable/base.py:1113\u001b[0m, in \u001b[0;36mRunnableSequence.invoke\u001b[0;34m(self, input, config)\u001b[0m\n\u001b[1;32m   1111\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m   1112\u001b[0m     \u001b[38;5;28;01mfor\u001b[39;00m i, step \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msteps):\n\u001b[0;32m-> 1113\u001b[0m         \u001b[38;5;28minput\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[43mstep\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m   1114\u001b[0m \u001b[43m            \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1115\u001b[0m \u001b[43m            \u001b[49m\u001b[38;5;66;43;03m# mark each step as a child run\u001b[39;49;00m\n\u001b[1;32m   1116\u001b[0m \u001b[43m            \u001b[49m\u001b[43mpatch_config\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m   1117\u001b[0m \u001b[43m                \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcallbacks\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrun_manager\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget_child\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43mf\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mseq:step:\u001b[39;49m\u001b[38;5;132;43;01m{\u001b[39;49;00m\u001b[43mi\u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[38;5;132;43;01m}\u001b[39;49;00m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m   1118\u001b[0m \u001b[43m            \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1119\u001b[0m \u001b[43m        \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m   1120\u001b[0m \u001b[38;5;66;03m# finish the root run\u001b[39;00m\n\u001b[1;32m   1121\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n","File \u001b[0;32m~/langchain/libs/langchain/langchain/schema/output_parser.py:173\u001b[0m, in \u001b[0;36mBaseOutputParser.invoke\u001b[0;34m(self, input, config)\u001b[0m\n\u001b[1;32m    169\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minvoke\u001b[39m(\n\u001b[1;32m    170\u001b[0m     \u001b[38;5;28mself\u001b[39m, \u001b[38;5;28minput\u001b[39m: Union[\u001b[38;5;28mstr\u001b[39m, BaseMessage], config: Optional[RunnableConfig] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m    171\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m T:\n\u001b[1;32m    172\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(\u001b[38;5;28minput\u001b[39m, BaseMessage):\n\u001b[0;32m--> 173\u001b[0m         \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_call_with_config\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m    174\u001b[0m \u001b[43m            \u001b[49m\u001b[38;5;28;43;01mlambda\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43minner_input\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparse_result\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m    175\u001b[0m \u001b[43m                \u001b[49m\u001b[43m[\u001b[49m\u001b[43mChatGeneration\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmessage\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43minner_input\u001b[49m\u001b[43m)\u001b[49m\u001b[43m]\u001b[49m\n\u001b[1;32m    176\u001b[0m \u001b[43m            \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    177\u001b[0m \u001b[43m            \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m    178\u001b[0m \u001b[43m            \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    179\u001b[0m \u001b[43m            \u001b[49m\u001b[43mrun_type\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mparser\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m    180\u001b[0m \u001b[43m        \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    181\u001b[0m     \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m    182\u001b[0m         \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_call_with_config(\n\u001b[1;32m    183\u001b[0m             \u001b[38;5;28;01mlambda\u001b[39;00m inner_input: \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mparse_result([Generation(text\u001b[38;5;241m=\u001b[39minner_input)]),\n\u001b[1;32m    184\u001b[0m             \u001b[38;5;28minput\u001b[39m,\n\u001b[1;32m    185\u001b[0m             config,\n\u001b[1;32m    186\u001b[0m             run_type\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mparser\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m    187\u001b[0m         )\n","File \u001b[0;32m~/langchain/libs/langchain/langchain/schema/runnable/base.py:633\u001b[0m, in \u001b[0;36mRunnable._call_with_config\u001b[0;34m(self, func, input, config, run_type, **kwargs)\u001b[0m\n\u001b[1;32m    626\u001b[0m run_manager \u001b[38;5;241m=\u001b[39m callback_manager\u001b[38;5;241m.\u001b[39mon_chain_start(\n\u001b[1;32m    627\u001b[0m     dumpd(\u001b[38;5;28mself\u001b[39m),\n\u001b[1;32m    628\u001b[0m     \u001b[38;5;28minput\u001b[39m,\n\u001b[1;32m    629\u001b[0m     run_type\u001b[38;5;241m=\u001b[39mrun_type,\n\u001b[1;32m    630\u001b[0m     name\u001b[38;5;241m=\u001b[39mconfig\u001b[38;5;241m.\u001b[39mget(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrun_name\u001b[39m\u001b[38;5;124m\"\u001b[39m),\n\u001b[1;32m    631\u001b[0m )\n\u001b[1;32m    632\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 633\u001b[0m     output \u001b[38;5;241m=\u001b[39m \u001b[43mcall_func_with_variable_args\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m    634\u001b[0m \u001b[43m        \u001b[49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrun_manager\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\n\u001b[1;32m    635\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m    636\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m    637\u001b[0m     run_manager\u001b[38;5;241m.\u001b[39mon_chain_error(e)\n","File \u001b[0;32m~/langchain/libs/langchain/langchain/schema/runnable/config.py:173\u001b[0m, in \u001b[0;36mcall_func_with_variable_args\u001b[0;34m(func, input, run_manager, config, **kwargs)\u001b[0m\n\u001b[1;32m    171\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m accepts_run_manager(func):\n\u001b[1;32m    172\u001b[0m     kwargs[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrun_manager\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m run_manager\n\u001b[0;32m--> 173\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n","File \u001b[0;32m~/langchain/libs/langchain/langchain/schema/output_parser.py:174\u001b[0m, in \u001b[0;36mBaseOutputParser.invoke.<locals>.<lambda>\u001b[0;34m(inner_input)\u001b[0m\n\u001b[1;32m    169\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minvoke\u001b[39m(\n\u001b[1;32m    170\u001b[0m     \u001b[38;5;28mself\u001b[39m, \u001b[38;5;28minput\u001b[39m: Union[\u001b[38;5;28mstr\u001b[39m, BaseMessage], config: Optional[RunnableConfig] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m    171\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m T:\n\u001b[1;32m    172\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(\u001b[38;5;28minput\u001b[39m, BaseMessage):\n\u001b[1;32m    173\u001b[0m         \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_call_with_config(\n\u001b[0;32m--> 174\u001b[0m             \u001b[38;5;28;01mlambda\u001b[39;00m inner_input: \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparse_result\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m    175\u001b[0m \u001b[43m                \u001b[49m\u001b[43m[\u001b[49m\u001b[43mChatGeneration\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmessage\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43minner_input\u001b[49m\u001b[43m)\u001b[49m\u001b[43m]\u001b[49m\n\u001b[1;32m    176\u001b[0m \u001b[43m            \u001b[49m\u001b[43m)\u001b[49m,\n\u001b[1;32m    177\u001b[0m             \u001b[38;5;28minput\u001b[39m,\n\u001b[1;32m    178\u001b[0m             config,\n\u001b[1;32m    179\u001b[0m             run_type\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mparser\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m    180\u001b[0m         )\n\u001b[1;32m    181\u001b[0m     \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m    182\u001b[0m         \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_call_with_config(\n\u001b[1;32m    183\u001b[0m             \u001b[38;5;28;01mlambda\u001b[39;00m inner_input: \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mparse_result([Generation(text\u001b[38;5;241m=\u001b[39minner_input)]),\n\u001b[1;32m    184\u001b[0m             \u001b[38;5;28minput\u001b[39m,\n\u001b[1;32m    185\u001b[0m             config,\n\u001b[1;32m    186\u001b[0m             run_type\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mparser\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m    187\u001b[0m         )\n","File \u001b[0;32m~/langchain/libs/langchain/langchain/schema/output_parser.py:225\u001b[0m, in \u001b[0;36mBaseOutputParser.parse_result\u001b[0;34m(self, result, partial)\u001b[0m\n\u001b[1;32m    212\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mparse_result\u001b[39m(\u001b[38;5;28mself\u001b[39m, result: List[Generation], \u001b[38;5;241m*\u001b[39m, partial: \u001b[38;5;28mbool\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mFalse\u001b[39;00m) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m T:\n\u001b[1;32m    213\u001b[0m \u001b[38;5;250m    \u001b[39m\u001b[38;5;124;03m\"\"\"Parse a list of candidate model Generations into a specific format.\u001b[39;00m\n\u001b[1;32m    214\u001b[0m \n\u001b[1;32m    215\u001b[0m \u001b[38;5;124;03m    The return value is parsed from only the first Generation in the result, which\u001b[39;00m\n\u001b[0;32m   (...)\u001b[0m\n\u001b[1;32m    223\u001b[0m \u001b[38;5;124;03m        Structured output.\u001b[39;00m\n\u001b[1;32m    224\u001b[0m \u001b[38;5;124;03m    \"\"\"\u001b[39;00m\n\u001b[0;32m--> 225\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparse\u001b[49m\u001b[43m(\u001b[49m\u001b[43mresult\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtext\u001b[49m\u001b[43m)\u001b[49m\n","File \u001b[0;32m~/langchain/libs/langchain/langchain/chains/query_constructor/base.py:60\u001b[0m, in \u001b[0;36mStructuredQueryOutputParser.parse\u001b[0;34m(self, text)\u001b[0m\n\u001b[1;32m     56\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m StructuredQuery(\n\u001b[1;32m     57\u001b[0m         \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m{k: v \u001b[38;5;28;01mfor\u001b[39;00m k, v \u001b[38;5;129;01min\u001b[39;00m parsed\u001b[38;5;241m.\u001b[39mitems() \u001b[38;5;28;01mif\u001b[39;00m k \u001b[38;5;129;01min\u001b[39;00m allowed_keys}\n\u001b[1;32m     58\u001b[0m     )\n\u001b[1;32m     59\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[0;32m---> 60\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m OutputParserException(\n\u001b[1;32m     61\u001b[0m         \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mParsing text\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;132;01m{\u001b[39;00mtext\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124m raised following error:\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;132;01m{\u001b[39;00me\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m     62\u001b[0m     )\n","\u001b[0;31mOutputParserException\u001b[0m: Parsing text\n```json\n{\n    \"query\": \"highly rated, coast, patio, fireplace\",\n    \"filter\": \"and(eq(\\\"starrating\\\", 4), contain(\\\"description\\\", \\\"coast\\\"), contain(\\\"description\\\", \\\"patio\\\"), contain(\\\"description\\\", \\\"fireplace\\\"))\"\n}\n```\n raised following error:\nReceived invalid attributes description. Allowed attributes are ['onsiterate', 'maxoccupancy', 'city', 'country', 'starrating', 'mealsincluded']"]}],"source":["# 调用chain的invoke方法，传入一个字典作为参数\n","chain.invoke(\n","    {\n","        \"query\": \"I want to stay somewhere highly rated along the coast. I want a room with a patio and a fireplace.\"\n","    }\n",")"]},{"cell_type":"markdown","id":"c845a5e3-9a4c-4f8d-b5af-6493fd0186cb","metadata":{},"source":["## 自动忽略无效查询\n","\n","看起来我们的模型在这个更复杂的查询上出现了问题，并尝试搜索一个不存在的属性（'description'）。通过在我们的查询构造器链中设置 `fix_invalid=True`，我们可以自动移除任何无效的筛选条件（即使用了不允许的操作、比较或属性）。"]},{"cell_type":"code","execution_count":22,"id":"fff986c4-ba52-4619-afdb-b0545834c0f8","metadata":{},"outputs":[],"source":["# 导入所需的库\n","from openai import ChatOpenAI\n","\n","# 调用load_query_constructor_runnable函数，加载查询构造器\n","chain = load_query_constructor_runnable(\n","    ChatOpenAI(model=\"gpt-3.5-turbo\", temperature=0),  # 使用ChatOpenAI模型，选择\"gpt-3.5-turbo\"模型，设置温度为0\n","    doc_contents,  # 文档内容\n","    filter_attribute_info,  # 过滤属性信息\n","    examples=examples,  # 示例\n","    fix_invalid=True,  # 修复无效值\n",")"]},{"cell_type":"code","execution_count":23,"id":"bdafa338-ca2f-4587-9457-472a6b9a9b27","metadata":{},"outputs":[{"data":{"text/plain":["StructuredQuery(query='highly rated, coast, patio, fireplace', filter=Comparison(comparator=<Comparator.EQ: 'eq'>, attribute='starrating', value=4), limit=None)"]},"execution_count":23,"metadata":{},"output_type":"execute_result"}],"source":["# 调用chain的invoke方法，传入一个包含查询信息的字典作为参数\n","chain.invoke(\n","    {\n","        \"query\": \"I want to stay somewhere highly rated along the coast. I want a room with a patio and a fireplace.\"\n","    }\n",")"]},{"cell_type":"markdown","id":"8251d117-8406-48b1-b331-0fe597b57051","metadata":{},"source":["## 使用自查询检索器\n","\n","现在我们的查询构造链已经基本就位，让我们尝试将其与实际的检索器一起使用。在这个例子中，我们将使用[ElasticsearchStore](https://python.langchain.com/docs/integrations/vectorstores/elasticsearch)。"]},{"cell_type":"code","execution_count":24,"id":"06f30efe-f96a-4baa-9571-1de01596a5ac","metadata":{},"outputs":[],"source":["from langchain_elasticsearch import ElasticsearchStore  # 导入ElasticsearchStore模块\n","from langchain_openai import OpenAIEmbeddings  # 导入OpenAIEmbeddings模块\n","\n","embeddings = OpenAIEmbeddings()  # 创建OpenAIEmbeddings对象并赋值给embeddings变量"]},{"cell_type":"markdown","id":"e468e0f6-fc1b-42ab-bf88-7088d8e1aad0","metadata":{},"source":["## 填充向量存储\n","\n","第一次运行时，请取消下面单元格的注释，首先对数据进行索引。"]},{"cell_type":"code","execution_count":25,"id":"1f73c1ff-bdb4-4c27-bfa3-c15a1b886244","metadata":{},"outputs":[],"source":["# 创建一个空列表docs用于存储文档\n","docs = []\n","\n","# 遍历latest_price中的每一行数据，_表示索引，room表示每一行的数据\n","for _, room in latest_price.fillna(\"\").iterrows():\n","    # 创建一个Document对象，其中page_content是将room转换为字典后使用json.dumps转换为字符串的结果，\n","    # metadata是room转换为字典的结果\n","    doc = Document(\n","        page_content=json.dumps(room.to_dict(), indent=2),\n","        metadata=room.to_dict()\n","    )\n","    # 将doc添加到docs列表中\n","    docs.append(doc)\n","\n","# 使用ElasticsearchStore.from_documents方法将docs中的文档存储到Elasticsearch中\n","vecstore = ElasticsearchStore.from_documents(\n","    docs,\n","    embeddings,\n","    es_url=\"http://localhost:9200\",\n","    index_name=\"hotel_rooms\",\n","    # strategy=ElasticsearchStore.ApproxRetrievalStrategy(\n","    #     hybrid=True,\n","    # )\n",")"]},{"cell_type":"code","execution_count":26,"id":"411af3ff-29e2-4042-9060-15f75c4fa0e9","metadata":{},"outputs":[],"source":["# 创建一个名为\"hotel_rooms\"的ElasticsearchStore对象\n","# 参数embedding指定嵌入向量\n","# 参数es_url指定Elasticsearch的URL地址\n","vecstore = ElasticsearchStore(\n","    \"hotel_rooms\",\n","    embedding=embeddings,\n","    es_url=\"http://localhost:9200\",\n","    # strategy=ElasticsearchStore.ApproxRetrievalStrategy(hybrid=True) # 在社区版本中似乎不可用\n",")"]},{"cell_type":"code","execution_count":27,"id":"309490df-5a5f-4ff6-863b-5a85b8811b44","metadata":{},"outputs":[],"source":["from langchain.retrievers import SelfQueryRetriever\n","\n","retriever = SelfQueryRetriever(\n","    query_constructor=chain, vectorstore=vecstore, verbose=True\n",")"]},{"cell_type":"code","execution_count":28,"id":"3e6aaca9-dd22-403b-8714-23b20137f483","metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":["{\n","  \"roomtype\": \"Three-Bedroom House With Sea View\",\n","  \"onsiterate\": 341.75,\n","  \"roomamenities\": \"Additional bathroom: ;Additional toilet: ;Air conditioning: ;Closet: ;Clothes dryer: ;Coffee/tea maker: ;Dishwasher: ;DVD/CD player: ;Fireplace: ;Free Wi-Fi in all rooms!: ;Full kitchen: ;Hair dryer: ;Heating: ;High chair: ;In-room safe box: ;Ironing facilities: ;Kitchenware: ;Linens: ;Microwave: ;Private entrance: ;Refrigerator: ;Seating area: ;Separate dining area: ;Smoke detector: ;Sofa: ;Towels: ;TV [flat screen]: ;Washing machine: ;\",\n","  \"maxoccupancy\": 6,\n","  \"roomdescription\": \"Room size: 125 m\\u00b2/1345 ft\\u00b2, 2 bathrooms, Shower and bathtub, Shared bathroom, Kitchenette, 3 bedrooms, 1 double bed or 2 single beds or 1 double bed\",\n","  \"hotelname\": \"Downings Coastguard Cottages - Type B-E\",\n","  \"city\": \"Downings\",\n","  \"country\": \"Ireland\",\n","  \"starrating\": 4,\n","  \"mealsincluded\": false\n","}\n","\n","--------------------\n","\n","{\n","  \"roomtype\": \"Three-Bedroom House With Sea View\",\n","  \"onsiterate\": 774.05,\n","  \"roomamenities\": \"Additional bathroom: ;Additional toilet: ;Air conditioning: ;Closet: ;Clothes dryer: ;Coffee/tea maker: ;Dishwasher: ;DVD/CD player: ;Fireplace: ;Free Wi-Fi in all rooms!: ;Full kitchen: ;Hair dryer: ;Heating: ;High chair: ;In-room safe box: ;Ironing facilities: ;Kitchenware: ;Linens: ;Microwave: ;Private entrance: ;Refrigerator: ;Seating area: ;Separate dining area: ;Smoke detector: ;Sofa: ;Towels: ;TV [flat screen]: ;Washing machine: ;\",\n","  \"maxoccupancy\": 6,\n","  \"roomdescription\": \"Room size: 125 m\\u00b2/1345 ft\\u00b2, 2 bathrooms, Shower and bathtub, Shared bathroom, Kitchenette, 3 bedrooms, 1 double bed or 2 single beds or 1 double bed\",\n","  \"hotelname\": \"Downings Coastguard Cottages - Type B-E\",\n","  \"city\": \"Downings\",\n","  \"country\": \"Ireland\",\n","  \"starrating\": 4,\n","  \"mealsincluded\": false\n","}\n","\n","--------------------\n","\n","{\n","  \"roomtype\": \"Four-Bedroom Apartment with Sea View\",\n","  \"onsiterate\": 501.24,\n","  \"roomamenities\": \"Additional toilet: ;Air conditioning: ;Carpeting: ;Cleaning products: ;Closet: ;Clothes dryer: ;Clothes rack: ;Coffee/tea maker: ;Dishwasher: ;DVD/CD player: ;Fireplace: ;Free Wi-Fi in all rooms!: ;Full kitchen: ;Hair dryer: ;Heating: ;High chair: ;In-room safe box: ;Ironing facilities: ;Kitchenware: ;Linens: ;Microwave: ;Private entrance: ;Refrigerator: ;Seating area: ;Separate dining area: ;Smoke detector: ;Sofa: ;Toiletries: ;Towels: ;TV [flat screen]: ;Wake-up service: ;Washing machine: ;\",\n","  \"maxoccupancy\": 9,\n","  \"roomdescription\": \"Room size: 110 m\\u00b2/1184 ft\\u00b2, Balcony/terrace, Shower and bathtub, Kitchenette, 4 bedrooms, 1 single bed or 1 queen bed or 1 double bed or 2 single beds\",\n","  \"hotelname\": \"1 Elliot Terrace\",\n","  \"city\": \"Plymouth\",\n","  \"country\": \"United Kingdom\",\n","  \"starrating\": 4,\n","  \"mealsincluded\": false\n","}\n","\n","--------------------\n","\n","{\n","  \"roomtype\": \"Three-Bedroom Holiday Home with Terrace and Sea View\",\n","  \"onsiterate\": 295.83,\n","  \"roomamenities\": \"Air conditioning: ;Dishwasher: ;Free Wi-Fi in all rooms!: ;Full kitchen: ;Heating: ;In-room safe box: ;Kitchenware: ;Private entrance: ;Refrigerator: ;Satellite/cable channels: ;Seating area: ;Separate dining area: ;Sofa: ;Washing machine: ;\",\n","  \"maxoccupancy\": 1,\n","  \"roomdescription\": \"Room size: 157 m\\u00b2/1690 ft\\u00b2, Balcony/terrace, 3 bathrooms, Shower, Kitchenette, 3 bedrooms, 1 queen bed or 1 queen bed or 1 queen bed or 1 sofa bed\",\n","  \"hotelname\": \"Seaside holiday house Artatore (Losinj) - 17102\",\n","  \"city\": \"Mali Losinj\",\n","  \"country\": \"Croatia\",\n","  \"starrating\": 4,\n","  \"mealsincluded\": false\n","}\n","\n","--------------------\n","\n"]}],"source":["# 调用retriever的invoke方法，并传入一个字符串作为参数\n","results = retriever.invoke(\n","    \"I want to stay somewhere highly rated along the coast. I want a room with a patio and a fireplace.\"\n",")\n","\n","# 遍历results中的每个结果\n","for res in results:\n","    # 打印结果的页面内容\n","    print(res.page_content)\n","    # 打印分隔线\n","    print(\"\\n\" + \"-\" * 20 + \"\\n\")"]},{"cell_type":"code","execution_count":null,"id":"8adec291-5853-4d2d-ab5d-294164f07f73","metadata":{},"outputs":[],"source":[]}],"metadata":{"kernelspec":{"display_name":"poetry-venv","language":"python","name":"poetry-venv"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.9.1"}},"nbformat":4,"nbformat_minor":5}
